﻿//-----------------------------------------------------------------------
//  Rewrite it from Google ARCore ObjectManipulation
//  Davidwang 2019.09.15
//-----------------------------------------------------------------------

namespace Davidwang
{
    using UnityEngine;
    using UnityEngine.XR.ARFoundation;
    using UnityEngine.XR.ARSubsystems;
    using System.Collections;
    using System.Collections.Generic;
    /// <summary>
    /// Manipulates the position of an object via a drag gesture.
    /// If not selected, the object will be selected when the drag gesture starts.
    /// </summary>
    public class TranslationManipulator : Manipulator
    {

        /// <summary>
        /// The translation mode of this object.
        /// </summary>
        public TranslationMode ObjectTranslationMode;

        /// <summary>
        /// The maximum translation distance of this object.
        /// </summary>
        public float MaxTranslationDistance = 5.0f;

        private const float k_PositionSpeed = 12.0f;
        private const float k_DiffThreshold = 0.0001f;

        private bool m_IsActive = false;
        private Vector3 m_DesiredPosition;
        private Quaternion m_DesiredRotation;
        private ARRaycastManager mRaycastManager;
        private List<ARRaycastHit> mHits = new List<ARRaycastHit>();
        private ARRaycastHit mLastHit;

        /// <summary>
        /// Translation mode.
        /// </summary>
        public enum TranslationMode
        {
            Horizontal,
            Vertical,
            Any,
        }

        /// <summary>
        /// The Unity's Start method.
        /// </summary>
        protected void Start()
        {
            m_DesiredPosition = new Vector3(0, 0, 0);
            mRaycastManager = GameObject.Find("AR Session Origin").GetComponent<ARRaycastManager>();
        }

        /// <summary>
        /// The Unity's Update method.
        /// </summary>
        protected override void Update()
        {
            base.Update();
            UpdatePosition();
        }

        /// <summary>
        /// Returns true if the manipulation can be started for the given gesture.
        /// </summary>
        /// <param name="gesture">The current gesture.</param>
        /// <returns>True if the manipulation can be started.</returns>
        protected override bool CanStartManipulationForGesture(DragGesture gesture)
        {
            if (gesture.TargetObject == null)
            {
                return false;
            }

            // If the gesture isn't targeting this item, don't start manipulating.
            if (gesture.TargetObject != gameObject)
            {
                return false;
            }

            // Select it.
            Select();

            return true;
        }

        /// <summary>
        /// Continues the translation.
        /// </summary>
        /// <param name="gesture">The current gesture.</param>
        protected override void OnContinueManipulation(DragGesture gesture)
        {
            m_IsActive = true;
            
            if (mRaycastManager.Raycast(gesture.Position, mHits, TrackableType.PlaneWithinPolygon))
            {
                m_DesiredPosition = mHits[0].pose.position;

                // Rotate if the plane direction has changed.
                if (((mHits[0].pose.rotation * Vector3.up) - transform.up).magnitude > k_DiffThreshold)
                {
                    m_DesiredRotation = mHits[0].pose.rotation;
                }
                else
                {
                    m_DesiredRotation = transform.rotation;
                }

                // If desired position is lower than current position, don't drop it until it's finished.
                /*
                if (m_DesiredPosition.y < transform.position.y)
                {
                    m_DesiredPosition.y = transform.position.y;
                }
                */
                if (m_DesiredPosition.magnitude > MaxTranslationDistance)
                {
                    m_DesiredPosition = m_DesiredPosition.normalized * MaxTranslationDistance;
                }

                mLastHit = mHits[0];


            }
        }

        /// <summary>
        /// Finishes the translation.
        /// </summary>
        /// <param name="gesture">The current gesture.</param>
        protected override void OnEndManipulation(DragGesture gesture)
        {
            if (mLastHit != null)
            {
                Pose desiredPose = mLastHit.pose;

                Vector3 desiredPosition = mLastHit.pose.position;

                if (desiredPosition.magnitude > MaxTranslationDistance)
                {
                    desiredPosition = desiredPosition.normalized * MaxTranslationDistance;
                }

                desiredPose.position = desiredPosition;

                // Rotate if the plane direction has changed.
                if (((desiredPose.rotation * Vector3.up) - transform.up).magnitude > k_DiffThreshold)
                {
                    m_DesiredRotation = desiredPose.rotation;
                }
                else
                {
                    m_DesiredRotation = transform.rotation;
                }
            }
        }

        private void UpdatePosition()
        {
            if (!m_IsActive)
            {
                return;
            }

            // Lerp position.
            Vector3 oldPosition = transform.position;
            Vector3 newPosition =
                Vector3.Lerp(oldPosition, m_DesiredPosition, Time.deltaTime * k_PositionSpeed);

            float diffLenght = (m_DesiredPosition - newPosition).magnitude;
            if (diffLenght < k_DiffThreshold)
            {
                newPosition = m_DesiredPosition;
                m_IsActive = false;
            }
            //Debug.Log("David:old=" + oldPosition + ",new =" + newPosition);
            transform.localPosition = newPosition;

            // Lerp rotation.
            Quaternion oldRotation = transform.rotation;
            Quaternion newRotation =
                Quaternion.Lerp(oldRotation, m_DesiredRotation, Time.deltaTime * k_PositionSpeed);

            transform.rotation = newRotation;
        }
        /*
        private bool IsPlaneTypeAllowed(DetectedPlaneType planeType)
        {
            if (ObjectTranslationMode == TranslationMode.Any)
            {
                return true;
            }

            if (ObjectTranslationMode == TranslationMode.Horizontal &&
               (planeType == DetectedPlaneType.HorizontalDownwardFacing ||
                planeType == DetectedPlaneType.HorizontalUpwardFacing))
            {
                return true;
            }

            if (ObjectTranslationMode == TranslationMode.Vertical &&
               planeType == DetectedPlaneType.Vertical)
            {
                return true;
            }

            return false;
        }
       */

    }
}
